package com.sunyard.manage.util.xmlUti;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Preconditions;
import com.sunyard.constant.BooleanConstant;
import com.sunyard.constant.ErrMessageConst;
import com.sunyard.constant.xml.XmlAttributeConstant;
import com.sunyard.exception.EsbServiceException;
import jdk.nashorn.internal.runtime.regexp.joni.Regex;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.*;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.StringUtils;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @description: xml工具类
 * @author: DUANYUBIN
 * @create: 2021-03-21 15:09
 **/
@Slf4j
public class XmlParseUtil {
    /**
     * xml对象
     */
    private final static Map<String, Document> DOCUMENT_MAP = new HashMap<>();

    public static Map<String, Document> getDocumentMap() {
        return DOCUMENT_MAP;
    }

    /**
     * 加载某个文件夹下的所有xml
     *
     * @param filePath 文件路径，需要以斜杠结尾
     * @throws IOException
     * @throws DocumentException
     */
    public static void loadXmlList(String filePath) throws IOException, DocumentException {
        //创建SAXReader对象
        SAXReader reader = new SAXReader();
        Resource[] resources = new PathMatchingResourcePatternResolver().getResources(filePath + "*.xml");
        for (Resource resource : resources) {
            InputStream inputStream = resource.getInputStream();
            //通过read的方法读取一个文件转换成Document对象
            final Document document = reader.read(inputStream);
            log.debug("加载XML：" + resource.getFilename());
            //存入缓存
            DOCUMENT_MAP.put(resource.getFilename(), document);
        }
    }

    /**
     * 流加载xml
     *
     * @author xiaog.li
     * @date 2019/5/15 11:00
     */
    @Deprecated
    static void loadDocument(InputStream inputStream, String fileName) throws DocumentException {
        log.debug("加载XML：" + fileName);
        //创建SAXReader对象
        SAXReader reader = new SAXReader();
        //通过read的方法读取一个文件转换成Document对象
        final Document document = reader.read(inputStream);
        //存入缓存
        DOCUMENT_MAP.put(fileName, document);
    }

    /**
     * 加载某个包路径下的所有xml
     *
     * @param filePath
     * @return
     */
    @Deprecated
    public static void loadDocument(String filePath) throws DocumentException {
        URL path = com.sunyard.utils.XmlParseUtil.class.getResource(filePath);
        //创建文件对象
        File fileDirectory = new File(path.getFile());
        if (fileDirectory.isDirectory()) {
            //判断是否为文件夹
            File[] files = fileDirectory.listFiles();
            for (File file : files) {
                log.debug("加载XML：" + file.getName());
                //创建SAXReader对象
                SAXReader reader = new SAXReader();
                //通过read的方法读取一个文件转换成Document对象
                Document document = reader.read(file);
                //存入缓存
                DOCUMENT_MAP.put(file.getName(), document);
            }
        }
    }

    /**
     * 报文交易 实体类与xml同节点
     *
     * @param object
     * @param fileName
     * @return
     * @throws Exception
     */
    public static boolean verify(Object object, String fileName) {
        log.debug("校验XML：" + fileName);
        Document document = DOCUMENT_MAP.get(fileName);
        Preconditions.checkArgument(document != null, "交易类型错误" + fileName);
        Element root = document.getRootElement();
        return getNodes(object, root);
    }


    /**
     * 递归判断
     *
     * @author xiaog.li
     * @date 2019/7/10 18:11
     */
    private static boolean getNodes(Object object, Element root) {
        //转json数据
        List<Element> elementList = root.elements();
        //判断是否有子节点
        if (elementList.size() > 0) {
            String json = JSON.toJSONString(object);
            //获取json对象
            JSONObject jsonObject = JSONObject.parseObject(json);
            //有子节点
            for (Element element : elementList) {
                Object obj = jsonObject.get(element.getName());
                Attribute nullable = element.attribute("nullable");
                Attribute nameAttr = element.attribute("name");
                //判断是否为空
                if (nullable != null && "false".equals(nullable.getValue())) {
                    if (StringUtils.isEmpty(obj)) {
                        log.debug("[" + element.getName() + ":" + nameAttr.getValue() + "]为空");
                        throw new NullPointerException("[" + element.getName() + ":" + nameAttr.getValue() + "]为空");
                    } else {
                        /*字段不为空，递归循环*/
                        if (!getNodes(obj, element)) {
                            return false;
                        }
                    }
                } else {
                    //属性不存在
                    if (StringUtils.isEmpty(obj)) {
                        //字段值为空 结束当前循环
                        continue;
                    } else {
                        //字段不为空，递归寻循环
                        if (!getNodes(obj, element)) {
                            return false;
                        }
                    }
                }

            }
        } else {
            Attribute nullable = root.attribute("nullable");
            Attribute maxlen = root.attribute("maxlen");
            Attribute nameAttr = root.attribute("name");
            if (nullable != null && "false".equals(nullable.getValue())) {
                if (StringUtils.isEmpty(object)) {
                    log.debug("[" + root.getName() + ":" + nameAttr.getValue() + "]为空");
                    throw new NullPointerException("[" + root.getName() + ":" + nameAttr.getValue() + "]为空");
                } else {
                    //判断长度
                    if (maxlen != null) {
                        Integer nMaxLen = Convert.toInt(maxlen.getValue());
                        Integer nCurLen = Convert.toStr(object).length();
                        if (nCurLen > nMaxLen) {
                            log.debug("[" + root.getName() + ":" + nameAttr.getValue() + "]长度不能超过" + nMaxLen + "字符");
                            throw new NullPointerException("[" + root.getName() + ":" + nameAttr.getValue() + "]长度不能超过" + nMaxLen + "字符");
                        }
                    }
                }
            } else {
                //可为空的情况
                //判断长度
                if (maxlen != null) {
                    Integer nMaxLen = Convert.toInt(maxlen.getValue());
                    Integer nCurLen = Convert.toStr(object).length();
                    if (nCurLen > nMaxLen) {
                        log.debug("[" + root.getName() + ":" + nameAttr.getValue() + "]长度不能超过" + nMaxLen + "字符");
                        throw new NullPointerException("[" + root.getName() + ":" + nameAttr.getValue() + "]长度不能超过" + nMaxLen + "字符");
                    }
                }
            }
        }
        return true;
    }

    /**
     * xml 转实体类
     *
     * @param xmlValue
     * @param clazz
     * @param <T>
     * @return
     * @throws Exception
     */
//    public static <T> T toBean(String xmlValue, Class<T> clazz) throws UnsupportedEncodingException, DocumentException, IllegalAccessException, InstantiationException, NoSuchFieldException {
//        //创建SAXReader对象
//        SAXReader reader = new SAXReader();
//        //通过read的方法读取一个文件转换成Document对象
//        Document document = reader.read(new ByteArrayInputStream(xmlValue.getBytes(StandardCharsets.UTF_8)));
//        //获取根节点元素
//        Element root = document.getRootElement();
//
//        T object = clazz.newInstance();
//        setFields(object, root);
//        return object;
//    }

    /**
     * 递归设置字段值
     *
     * @param object
     * @param root
     * @throws Exception
     */
//    public static void setFields(Object object, Element root) throws NoSuchFieldException, IllegalAccessException {
//        List<Element> elements = root.elements();
//        //判断是否有子节点
//        if (elements.size() > 0) {
//            //有，循环递归
//            for (Element element : elements) {
//                setFields(object, element);
//            }
//        } else {
//            setFieldValue(object, root.getName(), root.getTextTrim(), root);
//        }
//    }

    /**
     * 转xml
     *
     * @param object   实体
     * @param fileName 报文名称
     * @return
     * @throws UnsupportedEncodingException,DocumentException,NoSuchFieldException,IllegalAccessException
     */
    public static String toXml(Object object, String fileName) throws UnsupportedEncodingException, DocumentException, NoSuchFieldException, IllegalAccessException {
        //从缓存中读取xml文件
        Document initDocument = DOCUMENT_MAP.get(fileName);
        if (initDocument == null) {
            throw new NullPointerException("xml[" + fileName + "]对象不存在");
        }
        String xmlValue = initDocument.asXML().trim();
        //创建SAXReader对象
        SAXReader reader = new SAXReader();
        //通过read的方法读取一个文件转换成Document对象
        Document document = reader.read(new ByteArrayInputStream(xmlValue.getBytes("UTF-8")));
        if (document == null) {
            throw new NullPointerException(fileName + " xml对象不存在");
        }
        Element root = document.getRootElement();
        //xml赋值
        if (!setNodes(object, root)) {
            //表示报文验证错误
            return null;
        }
        //去除所有属性
        removeAttribute(root);
        //删除备注信息
        Iterator<Node> iterator = document.nodeIterator();
        while (iterator.hasNext()) {
            Node node = iterator.next();
            if (node.getNodeType() == Node.COMMENT_NODE) {
                //删除备注
                document.remove(node);
            }
        }
        deleteXmlNotation(document.getRootElement());

        //String xml = document.asXML();
        //使用对称标签
        String xml = asXml(document);
        return xml;
    }

    /**
     * 去掉注释
     *
     * @param ele
     */
    public static void deleteXmlNotation(Element ele) {
        Iterator<Node> nodes = ele.nodeIterator();
        List<Node> rmNodes = new ArrayList<Node>();
        //循环收集可以删除的节点
        while (nodes.hasNext()) {
            Node subNode = nodes.next();
            if (subNode.getNodeType() == Node.COMMENT_NODE) {
                //ele.remove(subNode);
                rmNodes.add(subNode);
                rmNodes.add(nodes.next());
            }
        }
        //删除收集到的节点
        for (Node node : rmNodes) {
            ele.remove(node);
        }
        //递归，处理下级节点
        Iterator<Element> eleIt = ele.elementIterator();
        while (eleIt.hasNext()) {
            deleteXmlNotation(eleIt.next());
        }
    }

    private static String asXml(Document document) {
        OutputFormat format = new OutputFormat();
        format.setEncoding(document.getXMLEncoding());
        format.setExpandEmptyElements(true);
        StringWriter out = new StringWriter();
        XMLWriter writer = new XMLWriter(out, format);
        try {
            writer.write(document);
            writer.flush();
        } catch (IOException e) {
            log.error(ErrMessageConst.UN_ESB_EXCEPTION, e);
        }
        String s = out.toString();
        return s;
    }

    /**
     * 递归遍历xml节点，并更改值
     * <p>
     * 实体类全部一级，xml有子节点
     *
     * @param object 实体类
     * @param root   父节点
     * @throws NoSuchFieldException,IllegalAccessException
     */
    public static boolean setNodes(Object object, Element root) throws NoSuchFieldException, IllegalAccessException {
        //判断type是否为list
        //type是list 为循环节点
        String type = root.attributeValue("type");
        if ("list".equals(type)) {
            return setListNodes(object, root, type);
        } else {
            //获取所有子节点
            List<Element> elements = root.elements();
            //判断是否有子节点
            if (elements.size() > 0) {
                //有，循环递归
                //去掉属性
//                removeAttribute(root);
                for (Element element : elements) {
                    if (!setNodes(object, element)) {
                        return false;
                    }
                }
            } else {
                //没有，更改字节点的值
                if (StringUtils.isEmpty(root.getTextTrim())) {
                    //当节点没有默认值时，更改值
                    Attribute attribute = root.attribute(XmlAttributeConstant.NULLABLE);
                    Attribute attributeParent = root.attribute(XmlAttributeConstant.PARENT);
                    Attribute nameAttr = root.attribute(XmlAttributeConstant.NAME);
                    Attribute maxLenAttr = root.attribute(XmlAttributeConstant.MAX_LEN);
                    Attribute minLenAttr = root.attribute(XmlAttributeConstant.MIN_LEN);
                    //判断是否为转换节点，转换节点的需要字段需要'_'同'-'
                    Attribute conversionAttr = root.attribute(XmlAttributeConstant.CONVERSION);
                    //判断节点是否为默认值
                    Attribute defaultValue = root.attribute(XmlAttributeConstant.DEFAULT_VALUE);

                    if (defaultValue != null && defaultValue.getValue() != null) {
                        //设置对应的参数值
                        root.setText(defaultValue.getValue());
                    } else {
                        if (attribute != null && BooleanConstant.FALSE_STRING.equals(attribute.getValue())) {
                            Object value = getValue(attributeParent, root, object, conversionAttr);
//                        if (StringUtils.isEmpty(value)) {
//                            log.info("[" + root.getName() + "  " + nameAttr.getValue() + "]不能为空");
//                            throw new NullPointerException("[" + root.getName() + "  " + nameAttr.getValue() + "]不能为空");
//                        }
                            checkMax(maxLenAttr, value, root, nameAttr);
                            checkMin(minLenAttr, value, root, nameAttr);
                            //设置对应的参数值
                            root.setText(String.valueOf(value));
                        } else {
                            //可以为空字段，如果java实体类中字段值不为空，则赋值
                            Object value = getValue(attributeParent, root, object, conversionAttr);
                            if (!StringUtils.isEmpty(value)) {
                                checkMax(maxLenAttr, value, root, nameAttr);
                                checkMin(minLenAttr, value, root, nameAttr);
                                //如果不为null设置值
                                root.setText(String.valueOf(value));
                            }
                        }
                    }
                }
                //移除添加的属性
//                removeAttribute(root);
            }
            return true;
        }

    }

    /**
     * 获取对应值
     *
     * @param attributeParent 父节点？
     * @param root            节点
     * @param object          实体类
     * @param conversionAttr  是否转换
     * @return value 值
     */
    private static Object getValue(Attribute attributeParent, Element root, Object object, Attribute conversionAttr) throws
            NoSuchFieldException, IllegalAccessException {
        Object value = null;
        if (attributeParent != null) {
            object = parentObj(root, object, attributeParent);
        }
        if (conversionAttr == null || "false".equals(conversionAttr.getValue())) {
            value = getFieldValue(object, root.getName());
        } else {
            value = getFieldValue(object, root.getName().replace('-', '_'));
        }

        return value;
    }

    /**
     * 递归获取Object
     */
    protected static Object parentObj(Element root, Object object, Attribute attributeParent) throws NoSuchFieldException, IllegalAccessException {
        Element parent = root.getParent();
        Attribute attributeParents = parent.attribute(XmlAttributeConstant.PARENT);
        if (attributeParents != null) {
            object = parentObj(parent, object, attributeParents);
        }
        return getFieldValue(object, attributeParent.getValue());
    }

    /**
     * 是否超过最大值
     *
     * @param maxLenAttr 最大长度
     * @param value      值
     * @param root       节点
     * @param nameAttr   name属性
     */
    protected static void checkMax(Attribute maxLenAttr, Object value, Element root, Attribute nameAttr) {
        if (maxLenAttr != null) {
            Integer maxLen = Convert.toInt(maxLenAttr.getValue(), 0);
            Integer curLen = Convert.toInt(Convert.toStr(value, "").length(), 0);
            if (curLen > maxLen) {
                log.debug("[" + root.getName() + "  " + nameAttr.getValue() + "]长度不能超过" + maxLen + "字符,当前值" + value + "长度为" + curLen + "字符");
                throw new NullPointerException("[" + root.getName() + "  " + nameAttr.getValue() + "]长度不能超过" + maxLen + "字符,当前值" + value + "长度为" + curLen + "字符");
            }
        }
    }

    /**
     * 是否超过最大值
     *
     * @param minLenAttr 最小长度
     * @param value      值
     * @param root       节点
     * @param nameAttr   name属性
     */
    protected static void checkMin(Attribute minLenAttr, Object value, Element root, Attribute nameAttr) {
        if (minLenAttr != null) {
            Integer minLen = Convert.toInt(minLenAttr.getValue(), 0);
            Integer curLen = Convert.toInt(Convert.toStr(value, "").length(), 0);
            if (curLen < minLen) {
                log.debug("[" + root.getName() + "  " + nameAttr.getValue() + "]长度不能少于" + minLen + "字符,当前值" + value + "长度为" + curLen + "字符");
                throw new NullPointerException("[" + root.getName() + "  " + nameAttr.getValue() + "]长度不能少于" + minLen + "字符,当前值" + value + "长度为" + curLen + "字符");
            }
        }
    }

    /**
     * 循环节点赋值
     *
     * @param object   实体
     * @param root     节点
     * @param nodeType type类型
     */
    private static Boolean setListNodes(Object object, Element root, String nodeType) throws
            NoSuchFieldException, IllegalAccessException {
        Object obj = null;
        Attribute attribute = root.attribute(XmlAttributeConstant.PARENT);
        object = parentObj(root, object, attribute);
        obj = getTValue(object, root.getName(), nodeType);
        List list = (List) obj;
        Element parent = root.getParent();
        parent.remove(root);
        Attribute type = root.attribute("type");
        root.remove(type);
        //数组长度
        int size = 0;
        if (list != null) {
            size = list.size();
        }
        for (int i = 0; i < size; i++) {
            root.addAttribute("index", Convert.toStr(i));
//            root.addAttribute(attribute.getName(),attribute.getValue());
            Element clone = (Element) root.clone();
            setNodes(list.get(i), clone);
            parent.add(clone);
        }


        return true;
    }


    /**
     * 移除属性
     *
     * @param root
     * @author xiaoGang
     */
    public static void removeAttribute(Element root) {
        List<Element> elements = root.elements();
        if (elements.size() > 0) {
            for (Element element : elements) {
                removeAttribute(element);
            }
        }
        List<Attribute> attributeList = new ArrayList<>();
        for (Attribute attribute : root.attributes()) {
            //移除属性
            attributeList.add(attribute);
        }
        for (Attribute attribute : attributeList) {
            root.remove(attribute);
        }
    }

    public static Field getField(Object object, String fileName) throws
            NoSuchFieldException, IllegalAccessException {
        Class clazz = object.getClass();
        List<Field> fieldsList = new ArrayList<>();  // 保存属性对象数组到列表
        while (clazz != null) {  // 遍历所有父类字节码对象
            Field[] declaredFields = clazz.getDeclaredFields();  // 获取字节码对象的属性对象数组
            for (Field fieldtmp : declaredFields) {
                fieldsList.add(fieldtmp);

            }
            clazz = clazz.getSuperclass();  // 获得父类的字节码对象
        }
        Field field = null;
        for (Field field1 : fieldsList) {
            if (field1.getName().equals(fileName)) {
                field = field1;
                break;
            }
        }
        return field;
    }

    public static Field getTField(Object object, String fileName, String nodeType) throws
            NoSuchFieldException, IllegalAccessException {
        Class clazz = object.getClass();
        // 保存属性对象数组到列表
        List<Field> fieldsList = new ArrayList<>();
        // 遍历所有父类字节码对象
        while (clazz != null) {
            // 获取字节码对象的属性对象数组
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field fieldtmp : declaredFields) {
                Class<?> type = fieldtmp.getType();
                if (type == java.util.List.class) {
                    fieldsList.add(fieldtmp);
                }
            }
            // 获得父类的字节码对象
            clazz = clazz.getSuperclass();
        }
        Field field = null;
        boolean flag = false;
        for (Field field1 : fieldsList) {
            Type genericType = field1.getGenericType();
            if (genericType == null) {
                continue;
            }
            if (flag) {
                break;
            }
            if (genericType instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) genericType;
                Type[] actualTypeArguments = pt.getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    String typeName = actualTypeArgument.getTypeName();
                    typeName = typeName.substring(typeName.lastIndexOf(".") + 1, typeName.length());
//                    if (typeName.equals(fileName)) {
                    field = field1;
                    flag = true;
                    break;
//                    }
                }

            }
        }
        return field;
    }

    /**
     * 获取字段值
     *
     * @param fileName
     * @return
     */
    public static Object getFieldValue(Object object, String fileName) throws
            NoSuchFieldException, IllegalAccessException {
        log.debug("获取对象属性:" + fileName);
        try {
            Field field = getField(object, fileName);
            field.setAccessible(true);
            return field.get(object);
        } catch (Exception e) {
            throw new EsbServiceException("对象属性：" + fileName + "获取异常");
        }
    }

    /**
     * 获取泛型
     *
     * @param object   实体类
     * @param fileName 节点名称
     * @param nodeType type类型
     * @return Object
     */
    public static Object getTValue(Object object, String fileName, String nodeType) throws
            NoSuchFieldException, IllegalAccessException {
        log.debug("获取对象属性:" + fileName);
        try {
            Field field = getTField(object, fileName, nodeType);

            field.setAccessible(true);
            return field.get(object);
        } catch (SecurityException e) {
            throw new EsbServiceException("对象属性：" + fileName + "获取异常");
        }
    }

    /**
     * 设置值
     *
     * @throws Exception
     */
//    public static void setFieldValue(Object object, String fileName, Object value, Element root) {
//        try {
//            log.info("获取属性 name=" + fileName);
//            Field field = getField(object, fileName);
//            if (field == null) {
//                //如果查不到Field，就去父节点去找
//                Object parentObj = getFieldValue(object, root.getParent().getName());
//                field = getField(parentObj, fileName);
//                object = parentObj;
//            }
//            //log.info("设置属性值 name="+field.getName()+" value="+value);
//            field.setAccessible(true);
//            if (field.getType() == Double.class) {
//                field.set(object, Convert.toDouble(value));
//            } else if (field.getType() == Integer.class) {
//                field.set(object, Convert.toInt(value));
//            } else if (field.getType() == Float.class) {
//                field.set(object, Convert.toFloat(value));
//            } else if (field.getType() == BigDecimal.class) {
//                field.set(object, Convert.toBigDecimal(value));
//            } else if (field.getType() == Date.class) {
//                field.set(object, Convert.toDate(value));
//            } else if (field.getType() == Long.class) {
//                field.set(object, Convert.toLong(value));
//            } else if (field.getType() == String.class) {
//                field.set(object, value);
//            } else {
//                field.set(object, value);
//            }
//
//        } catch (NoSuchFieldException e) {
//            log.warn("多渠道返回多余字段[" + fileName + "]该系统暂时无用");
//        } catch (IllegalAccessException e) {
//            e.printStackTrace();
//        }
//    }
    public static String mergeHostXml(String xmlName) {
        Document sent_host = DOCUMENT_MAP.get("send_HOST.xml");

        Element parent = sent_host.getRootElement();
        Element flows = parent.element("Body").element("request");

        Document read = DOCUMENT_MAP.get("send_HOST_" + xmlName + ".xml");
        List<Element> elements = read.getDocument().getRootElement().elements();
        for (Element emt : elements) {
            flows.add(emt.detach());
        }
        deleteXmlNotation(sent_host.getRootElement());
        return sent_host.asXML();

    }

    public static <T> T fromXml(String xml, Class<T> type) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(type);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        return (T) unmarshaller.unmarshal(new StringReader(xml));
    }

}
